package com.boyinet.imesh2json.netty.decoder

import com.boyinet.imesh2json.common.*
import com.boyinet.imesh2json.common.CustomProtocol.SlaveValue
import io.netty.buffer.ByteBuf
import io.netty.buffer.ByteBufUtil
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.ByteToMessageDecoder
import org.slf4j.Logger
import org.slf4j.LoggerFactory

/**
 * @author lengchunyun
 */
class IMeshMessageDecoder : ByteToMessageDecoder() {

    private val log: Logger = LoggerFactory.getLogger(IMeshMessageDecoder::class.java)

    companion object {
        /**
         * 到第一个数据段的长度，读取到部分有效信息的最短长度
         */
        private const val HEAD_LENGTH = 20

        /**
         * 帧头的长度 "AA3B"
         */
        private const val HEAD_TAG = 2

        /**
         * 固定帧头
         */
        private const val HEAD = "AA3B"

        /**
         * 数据包类型  数据类型
         */
        private const val INFO_TAG = 2

        /**
         * OID 10位
         */
        private const val SN_LENGTH = 10

        /**
         * 单位 + 小数位数 + 传感器类型  判断数据是否够长
         */
        private const val TYPE_LENGTH = 3
        private const val TWO_BYTE_LENGTH = 2
        private const val TERMINALS_MIN_BYTE_NUMBERS = 20
        private const val DATA_NUM_MIN_BYTE_NUMBERS = 6
    }


    /**
     * | head | | type |
     * AA 3B    03 00
     *
     * |NodeNum|  |            SN                  |
     *   01        30 30 30 30 30 30 30 30 30 34
     *
     * | signalStrength| |battery|  |alarm|
     *         BA           34        01
     *
     * |partNum|
     *    02
     *
     * |valueLength|  | unit | | decimal|  |sensorType| | value-1 | |value-2|
     *     04           00         01          00       04 2C         02 11
     *
     * |valueLength|  | unit | | decimal|  |sensorType| | value |
     *     02            02         01          00      04 0C
     */
    override fun decode(ctx: ChannelHandlerContext, message: ByteBuf, out: MutableList<Any>) {
        if (message.readableBytes() < HEAD_LENGTH) {
            log.error("数据为{}", ByteBufUtil.hexDump(message))
            return
        }
        log.info("数据为{}", ByteBufUtil.hexDump(message))
        //标记一下当前的readIndex的位置
        message.markReaderIndex()
        //读取帧头
        val head = message.readBytes(HEAD_TAG)
        val headTag = ByteBufUtil.hexDump(head)
        head.release()
        //读取 （数据包类型  数据类型）
        message.readBytes(INFO_TAG).release()
        //终端节点数目 值
        var temp = message.readByte()
        val terminalsNum = ByteUtil.toInt(temp)
        //设置
        val customProtocols: MutableList<CustomProtocol> = ArrayList(terminalsNum)
        //错误 不以AA3B起始
        if (!headTag.equals(HEAD, ignoreCase = true) || terminalsNum <= 0) {
            message.resetReaderIndex()
            log.error("数据为{}", ByteBufUtil.hexDump(message))
            message.clear()
            ctx.close()
        } else {
            if (message.readableBytes() < terminalsNum * TERMINALS_MIN_BYTE_NUMBERS) {
                message.resetReaderIndex()
                return
            }
            //遍历数据段
            for (i in 1..terminalsNum) {
                val customProtocol = buildTerminal(message, i, customProtocols)
                //终端i节点数据段数
                temp = message.readByte()
                val dataNum = ByteUtil.toInt(temp)
                if (message.readableBytes() < dataNum * DATA_NUM_MIN_BYTE_NUMBERS) {
                    message.resetReaderIndex()
                    return
                }
                //遍历节点

                for (j in 1..dataNum) {
                    //是否还有可读的字节
                    if (message.readableBytes() < 1) {
                        //长度不够,撤回到标记
                        if (j != dataNum) {
                            //不是该数据段最后的节点
                            message.resetReaderIndex()
                            return
                        }
                    } else {
                        //长度够了,第j段数据长度
                        temp = message.readByte()
                        val pointLength = ByteUtil.toInt(temp)
                        //判断可读长度是否够用 数据长度+数据信息长度
                        if (message.readableBytes() < pointLength + TYPE_LENGTH) {
                            message.resetReaderIndex()
                            return
                        } else {
                            addDataNumData(message, i, j, customProtocol, pointLength)
                        }
                    }
                }
            }
            out.add(customProtocols)
        }
        //读到的消息体长度如果小于我们传送过来的消息长度，则resetReaderIndex. 这个配合markReaderIndex使用的。把readIndex重置到mark的地方
    }

    private fun buildTerminal(message: ByteBuf, i: Int, customProtocols: MutableList<CustomProtocol>): CustomProtocol {
        //读取10位sn码
        val sn = ByteArray(SN_LENGTH)
        message.readBytes(sn)
        val currTerminal = CustomProtocol()
        currTerminal.segmentNum = i
        val result = removeZeroAtEnd(sn)
        currTerminal.sn = ModbusStrBuilder.addZeroForNum(ByteUtil.toAscii(*result), 10)
        log.info("[sn]:{}", currTerminal.sn)
        //处理添加额外的属性
        addExtendedAttr(message, currTerminal)
        customProtocols.add(currTerminal)
        return currTerminal
    }

    private fun removeZeroAtEnd(sn: ByteArray): ByteArray {
        var lastIndex = sn.size - 1
        while (true) {
            if (sn[lastIndex] == 0.toByte()) {
                lastIndex--
            } else {
                break
            }
        }
        val target = ByteArray(lastIndex + 1)
        System.arraycopy(sn, 0, target, 0, lastIndex + 1)
        return target
    }

    /**
     * @param message
     * @param nodeIndex
     * @param partIndex
     * @param customProtocol
     * @param pointLength
     *
     */
    private fun addDataNumData(
        message: ByteBuf, nodeIndex: Int, partIndex: Int,
        customProtocol: CustomProtocol, pointLength: Int
    ) {
        val unitId = message.readByte()
        //小数点 位数
        val decimalPlaces = message.readByte()
        //  传感器类型
        val sensorType = message.readByte()
        // 原始头长度 + 数据段长度 + 数据段节点长度 + 数据长度（3字节传感器类型+dataNum字节数据长度）
        var k = TWO_BYTE_LENGTH
        while (k <= pointLength) {
            val slaveValue = SlaveValue()
            slaveValue.value = message.readShort().toString()
            slaveValue.seniorType = sensorType.toInt()
            slaveValue.unit = "$sensorType,$unitId"
            slaveValue.decimalPlaces = decimalPlaces.toInt()
            customProtocol.data["${nodeIndex}:${partIndex}:${k / TWO_BYTE_LENGTH}"] = slaveValue
            k += TWO_BYTE_LENGTH
        }
    }

    /**
     * 为设备添加额外的参数
     *
     * @param message          字节队列缓存
     * @param customProtocol 设备
     */
    private fun addExtendedAttr(message: ByteBuf, customProtocol: CustomProtocol) {
        val attributes = ExtendedAttributes()
        val signalStrength = message.readByte()

        val batteryStatus = message.readByte()
        val binary = ByteUtil.toBinary(batteryStatus)

        with(attributes) {

            this.signalStrength = ExtendedAttributes.Item().apply {
                code = signalStrength.toString()
                value = "$signalStrength dbm"
            }

            powerSource = ExtendedAttributes.Item().apply {
                code = binary.substring(0, 1)
                value = GlobalInfo.POWER_SOURCE_MAP[binary.substring(0, 1)]
            }
            powerSupply = ExtendedAttributes.Item().apply {
                code = binary.substring(1, 3)
                value = GlobalInfo.POWER_SUPPLY_MAP[binary.substring(1, 3)]
            }
            batteryPower = ExtendedAttributes.Item().apply {
                code = (Integer.valueOf(binary.substring(3, 8), 2) * 5).toString()
                value = "$code %"
            }
        }
        val alarmStatus = message.readByte()
        val alarm = ByteUtil.toBinary(alarmStatus)
        val integers = ByteUtil.indexsOf(alarm, '1')
        var i = 1
        val len = integers.size
        val builder = StringBuilder()
        for (integer in integers) {
            if (integer == -1) {
                builder.append(IMeshConstant.NORMAL_INFO)
                break
            } else {
                builder.append(GlobalInfo.ALARM_STATUS_MAP[integer])
                if (i < len) {
                    builder.append(",")
                }
                i++
            }
        }
        attributes.alarmStatus = ExtendedAttributes.Item().apply {
            code = alarmStatus.toString()
            value = builder.toString()
        }
        customProtocol.attributes = attributes
    }

}